Кратко пробежаться по плану, отметить, что тема связана с предыдущими лекциями о процессах и планировании. Указать, что основное внимание — на проблемах синхронизации и состязаний. Практическое использование API объектов синхронизации (CreateMutex, CreateSemaphore, pthread_mutex) рассмотрено в лекции 3.
Нарисовать диаграмму на доске параллельно. Спросить студентов, какие ещё состояния возможны (например, зомби-состояние в Linux). Обратить внимание на переход timeout — это основа вытесняющего планирования.
Задать вопрос: «Может ли процесс перейти из состояния "Ожидание" сразу в "Выполняется"?» Ответ — да, если ресурс освободился и процессор свободен. Напомнить про различие между блокирующим и неблокирующим I/O.
Обратить внимание на ключевое слово volatile перед state — объяснить, почему оно здесь нужно (значение может меняться из другого контекста). Предложить студентам выполнить `cat /proc/<pid>/status` на практике и найти соответствующие поля.
Задать вопрос: «Почему программный счетчик и регистры хранятся именно в PCB, а не где-то ещё?» Подчеркнуть связь PCB с переключением контекста — эти данные нужны, чтобы корректно возобновить процесс. Упомянуть, что в Linux task_struct содержит ~400 полей.
Подчеркнуть, что переключение контекста — дорогая операция (сотни тактов), поэтому ядро старается минимизировать его частоту. Типичная ошибка студентов: путать контекст процесса с контекстом потока. Упомянуть системный вызов getcontext/setcontext из POSIX.
Спросить: «Приведите примеры независимых и взаимодействующих процессов из повседневного опыта». Ключевой переход: от независимых процессов к разделяемым данным и проблеме состязаний. Перечислить IPC — кратко, подробно каждый механизм будет в следующем разделе.
Обратить внимание на двухэтапную схему: сначала создаём объект (CreateFileMapping), затем отображаем в адресное пространство (MapViewOfFile). Важный момент: по имени "MySharedMem" другой процесс может открыть ту же память. Предупредить об ошибке: забывают вызвать UnmapViewOfFile — утечка адресного пространства.
Показать параллель между Windows и POSIX API. Объяснить разницу между IPC_PRIVATE и именованным ключом (ftok). Частая ошибка: не удаляют разделяемую память через shmctl — она остаётся в системе до перезагрузки. Показать команду ipcs для просмотра.
Ключевой слайд лекции. Три условия Дейкстры нужно выписать на доску и разобрать каждое. Спросить: «Что будет с counter++ при одновременном выполнении?» Показать разборку инструкции counter++ на чтение-модификацию-запись. Это основа для понимания состязаний.
Обязательно предложить студентам найти ошибки ДО показа ответа. Для решения 1 нарисовать временную диаграмму: оба процесса проверяют флаг другого, видят false, оба устанавливают свой флаг. Для решения 2 — что если процесс 0 завершился? Процесс 1 зависнет навсегда.
Разобрать алгоритм пошагово. Ключевая идея: flag[i]=true показывает «я хочу войти», turn=j показывает «но уступаю тебе». Задать вопрос: «Почему процесс не может одновременно войти в критическую секцию?» Доказательство корректности можно предложить в качестве домашнего задания.
Подчеркнуть, что cli/sti — привилегированные инструкции, доступны только ядру. На многоядерных системах они не помогают — другой ядро продолжит выполнение. Test-And-Set реализуется аппаратно как неделимая операция — на уровне шины с блокировкой (LOCK prefix на x86). Упомянуть CMPXCHG как основу CAS (compare-and-swap).
Важно различать мьютекс и семафор: мьютекс владеет один поток и только он может его отпустить; семафор может захватить один поток, а освободить — другой. Предупредить об ошибке: забыл unlock — дедлок. Упомянуть, что в pthreads есть trylock и timedlock.
Привести реальные примеры: принтер (производитель — приложения, потребитель — драйвер), очередь сообщений, конвейер в ОС. Задать вопрос: «Что будет, если производителей больше, чем потребителей?» Обратить внимание на кольцевой буфер — in и out обойдут границы.
Предложить студентам найти ошибку. Спросить: «Может ли count стать отрицательным?» Показать сценарий: оба процесса одновременно проверяют count и оба проходят проверку. Также отметить busy-waiting — процессор тратится впустую. Это подводит к семафорному решению.
ВАЖНО: порядок sem_wait имеет значение! Если поменять mutex и empty — возможен дедлок. Объяснить почему: процесс захватывает mutex, затем ждёт empty, но потребитель не может уменьшить full, потому что mutex занят. Задать вопрос студентам про порядок sem_post — имеет ли он значение? (Нет, но лучше сначала освободить mutex.)
Монитор автоматически обеспечивает взаимное исключение — студентам не нужно явно управлять блокировками. Сравнить с семафорным решением: код проще, но монитор доступен не во всех языках (Java synchronized — ближайший аналог). Упомянуть, что Java реализует мониторы через wait/notify/notifyAll.
Первый читатель блокирует писателя (wrt), последний читатель разблокирует. Проблема приоритета читателей: писатель может «голодать», если читатели приходят постоянно. Для приоритета писателей нужен более сложный механизм с дополнительным семафором на входе читателей.
Иерархия ресурсов — самый простой способ предотвращения дедлока. Каждый философ нумерует вилки и берёт младшую первой. Альтернативы: 1) ограничить число одновременно сидящих философов (семафор на 4), 2) философ берёт обе вилки атомарно (арбитр), 3) нечётные берут сначала левую, чётные — правую.
Три семафора/мьютекса: customers — будит парикмахера, barber — клиент ждёт пока парикмахер освободится, mutex — защита waiting. Ключевой момент: проверка waiting < CHAIRS должна быть под мьютексом, иначе два клиента могут занять одно «место».
Пробежаться по каждому пункту, задавая студентам вопросы для проверки понимания. Особый акцент на три условия Дейкстры и порядок захвата семафоров в producers-consumers.
Предложить студентам ответить на вопросы устно. Наиболее сложные — вопросы 3, 4 и 5. Если остаются вопросы по алгоритму Петерсона, повторить доказательство на доске.
Задание 4 — хорошее введение: запустить два потока, каждый увеличивает счётчик на миллион, сравнить ожидаемый результат с фактическим. Задания 1 и 3 — основная работа. Задание 2 — повторение IPC. Рекомендовать использовать pthreads и sem_t.